package level.model.utility;

import java.awt.Component;
import java.awt.HeadlessException;
import java.io.File;
import java.util.ArrayList;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import level.model.Collision;
import level.model.GameObject;
import level.model.State;
import level.model.StateCollisionLayer;
import level.model.StateLayer;
import level.model.StateObjectLayer;
import level.model.StateTileLayer;
import level.model.Tile;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

public class StateSaver
{
	public static final String LAYER_NODE_NAME = "Layer";
	public static final String MAP_NODE_NAME = "Map";
	public static final String INFO_NODE_NAME = "Info";
	public static final String NAME_NODE_NAME = "Name";
	public static final String VALUE_NODE_NAME = "Value";
	public static final String Z_ORDER_NODE_NAME = "Z-Order";
	public static final String TILE_NODE_NAME = "Tile";
	public static final String IMAGE_NODE_NAME = "Image";
	public static final String COLLISION_NODE_NAME = "Collision-Line";
	public static final String COLLISION_LAYER_NODE_NAME = "Collision";
	public static final String OBJECT_LAYER_NODE_NAME = "Object";
	public static final String OBJECT_NODE_NAME = "Game-Object";
	public static final String COMPONENT_NODE_NAME = "Component";
	public static final String ATTRIBUTE_NODE_NAME = "Attribute";

	public static void save(State state)
	{
		try
		{
			DocumentBuilderFactory dbFactory = DocumentBuilderFactory
					.newInstance();
			DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();

			Document doc = dBuilder.newDocument();
			Element rootElement = doc.createElement(MAP_NODE_NAME);
			doc.appendChild(rootElement);

			Element infoElement = doc.createElement(INFO_NODE_NAME);
			rootElement.appendChild(infoElement);

			Element nameEl = doc.createElement(NAME_NODE_NAME);
			nameEl.appendChild(doc.createTextNode(state.getFile().getName()));
			infoElement.appendChild(nameEl);

			Element layerEl = doc.createElement(LAYER_NODE_NAME);
			rootElement.appendChild(layerEl);

			state.getLayers();

			for (Integer i : state.getLayers().keySet())
			{
				StateLayer layer = state.getLayers().get(i);
				saveLayer(layer, doc, layerEl);
			}

			TransformerFactory transformerFactory = TransformerFactory
					.newInstance();
			Transformer transformer = transformerFactory.newTransformer();
			transformer.setOutputProperty(OutputKeys.INDENT, "yes");
			transformer.setOutputProperty(
					"{http://xml.apache.org/xslt}indent-amount", "4");
			DOMSource source = new DOMSource(doc);
			StreamResult result = new StreamResult(state.getFile());
			transformer.transform(source, result);
		}
		catch (Exception e)
		{

		}
	}

	public static boolean saveAs(State state)
	{
		FileFilter filter = new FileNameExtensionFilter("XML Files (*.xml)",
				"xml");
		JFileChooser fc = new JFileChooser(state.getFile().getParent())
		{
			public File getSelectedFile()
			{
				File selectedFile = super.getSelectedFile();

				if (selectedFile != null)
				{
					String name = selectedFile.getName();
					if (!name.endsWith(".xml"))
					{
						selectedFile = new File(selectedFile.getParentFile(),
								name + ".xml");
					}
				}
				return selectedFile;
			}

			protected JDialog createDialog(Component parent)
					throws HeadlessException
			{
				JDialog dlg = super.createDialog(parent);
				dlg.setLocation(20 + dlg.getLocation().x,
						20 + dlg.getLocation().y);
				return dlg;
			}

			public void approveSelection()
			{
				if (getDialogType() == SAVE_DIALOG)
				{
					File selectedFile = getSelectedFile();
					if ((selectedFile != null) && selectedFile.exists())
					{
						int response = JOptionPane
								.showConfirmDialog(
										this,
										"The file "
												+ selectedFile.getName()
												+ " already exists. Do you want to replace the existing file?",
										"Ovewrite file",
										JOptionPane.YES_NO_OPTION,
										JOptionPane.WARNING_MESSAGE);
						if (response != JOptionPane.YES_OPTION)
							return;
					}
				}
				super.approveSelection();
			}
		};
		fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
		fc.setFileFilter(filter);
		fc.setSelectedFile(state.getFile());

		int returnVal = fc.showSaveDialog(null);
		if (returnVal != JFileChooser.APPROVE_OPTION)
			return false;

		state.setFile(fc.getSelectedFile());
		save(state);

		return true;
	}

	private static void saveLayer(StateLayer layer, Document doc,
			Element rootElement)
	{
		if (layer instanceof StateTileLayer)
		{
			saveTileLayer((StateTileLayer) layer, doc, rootElement);
		}

		if (layer instanceof StateCollisionLayer)
		{
			saveCollisionLayer((StateCollisionLayer) layer, doc, rootElement);
		}

		if (layer instanceof StateObjectLayer)
		{
			saveObjectLayer((StateObjectLayer) layer, doc, rootElement);
		}

	}

	private static void saveTileLayer(StateTileLayer layer, Document doc,
			Element rootElement)
	{
		String layerName = layer.getName();
		Element layerNameEl = doc.createElement(layerName);
		rootElement.appendChild(layerNameEl);

		Element zOrder = doc.createElement(Z_ORDER_NODE_NAME);
		zOrder.appendChild(doc.createTextNode(Integer.toString(layer
				.getZOrder())));
		layerNameEl.appendChild(zOrder);
		for (Tile tile : layer.getTiles().values())
		{
			Element tileEl = doc.createElement(TILE_NODE_NAME);
			layerNameEl.appendChild(tileEl);

			Element image = doc.createElement(IMAGE_NODE_NAME);
			image.appendChild(doc.createTextNode(tile.getIconPath()));
			tileEl.appendChild(image);

			Element x = doc.createElement("x");
			x.appendChild(doc.createTextNode(Integer.toString(tile.getX())));
			tileEl.appendChild(x);

			Element y = doc.createElement("y");
			y.appendChild(doc.createTextNode(Integer.toString(tile.getY())));
			tileEl.appendChild(y);

			Element w = doc.createElement("w");
			w.appendChild(doc.createTextNode(Integer.toString(tile.getW())));
			tileEl.appendChild(w);

			Element h = doc.createElement("h");
			h.appendChild(doc.createTextNode(Integer.toString(tile.getH())));
			tileEl.appendChild(h);

			Element sx = doc.createElement("sx");
			sx.appendChild(doc.createTextNode(Integer.toString(tile.getSx())));
			tileEl.appendChild(sx);

			Element sy = doc.createElement("sy");
			sy.appendChild(doc.createTextNode(Integer.toString(tile.getSy())));
			tileEl.appendChild(sy);

			Element sw = doc.createElement("sw");
			sw.appendChild(doc.createTextNode(Integer.toString(tile.getSw())));
			tileEl.appendChild(sw);

			Element sh = doc.createElement("sh");
			sh.appendChild(doc.createTextNode(Integer.toString(tile.getSh())));
			tileEl.appendChild(sh);
		}
	}

	private static void saveCollisionLayer(StateCollisionLayer layer,
			Document doc, Element rootElement)
	{
		String layerName = layer.getName();
		ArrayList<Collision> collisions = layer.getCollisions();
		Element layerNameEl = doc.createElement(layerName);
		rootElement.appendChild(layerNameEl);

		Element zOrder = doc.createElement(Z_ORDER_NODE_NAME);
		zOrder.appendChild(doc.createTextNode(Integer.toString(layer
				.getZOrder())));
		layerNameEl.appendChild(zOrder);

		if (collisions.size() != 0)
		{
			for (Collision collision : collisions)
			{
				Element tileEl = doc.createElement(COLLISION_NODE_NAME);
				layerNameEl.appendChild(tileEl);

				Element x1 = doc.createElement("x1");
				x1.appendChild(doc.createTextNode(Integer
						.toString((int) collision.getLine().getX1())));
				tileEl.appendChild(x1);

				Element y1 = doc.createElement("y1");
				y1.appendChild(doc.createTextNode(Integer
						.toString((int) collision.getLine().getY1())));
				tileEl.appendChild(y1);

				Element x2 = doc.createElement("x2");
				x2.appendChild(doc.createTextNode(Integer
						.toString((int) collision.getLine().getX2())));
				tileEl.appendChild(x2);

				Element y2 = doc.createElement("y2");
				y2.appendChild(doc.createTextNode(Integer
						.toString((int) collision.getLine().getY2())));
				tileEl.appendChild(y2);
			}
		}
	}

	private static void saveObjectLayer(StateObjectLayer layer, Document doc,
			Element rootElement)
	{
		String layerName = layer.getName();
		ArrayList<GameObject> objects = layer.getObjects();
		Element layerNameEl = doc.createElement(layerName);
		rootElement.appendChild(layerNameEl);

		Element zOrder = doc.createElement(Z_ORDER_NODE_NAME);
		zOrder.appendChild(doc.createTextNode(Integer.toString(layer
				.getZOrder())));
		layerNameEl.appendChild(zOrder);

		if (objects.size() != 0)
		{
			for (GameObject g : objects)
			{
				Element oEl = doc.createElement(OBJECT_NODE_NAME);
				layerNameEl.appendChild(oEl);

				Element name = doc.createElement(NAME_NODE_NAME);
				name.appendChild(doc.createTextNode(g.getObjectName()));
				oEl.appendChild(name);

				Element image = doc.createElement(IMAGE_NODE_NAME);
				image.appendChild(doc.createTextNode(g.getIconPath()));
				oEl.appendChild(image);

				Element x = doc.createElement("x");
				x.appendChild(doc.createTextNode(Integer.toString(g.getX())));
				oEl.appendChild(x);

				Element y = doc.createElement("y");
				y.appendChild(doc.createTextNode(Integer.toString(g.getY())));
				oEl.appendChild(y);

				Element w = doc.createElement("w");
				w.appendChild(doc.createTextNode(Integer.toString(g.getW())));
				oEl.appendChild(w);

				Element h = doc.createElement("h");
				h.appendChild(doc.createTextNode(Integer.toString(g.getH())));
				oEl.appendChild(h);

				for (String cName : g.getComponents().keySet())
				{
					Element CE = doc.createElement(COMPONENT_NODE_NAME);
					oEl.appendChild(CE);

					Element ECName = doc.createElement(NAME_NODE_NAME);
					ECName.appendChild(doc.createTextNode(cName));
					CE.appendChild(ECName);

					for (String aName : g.getComponent(cName).getAttributes()
							.keySet())
					{
						Element AE = doc.createElement(ATTRIBUTE_NODE_NAME);
						CE.appendChild(AE);

						Element EAName = doc.createElement(NAME_NODE_NAME);
						EAName.appendChild(doc.createTextNode(aName));
						AE.appendChild(EAName);

						Element value = doc.createElement(VALUE_NODE_NAME);
						value.appendChild(doc.createTextNode(g
								.getComponent(cName).getAttributes().get(aName)
								.getValue()));
						AE.appendChild(value);
					}
				}
			}
		}
	}
}
